package com.yeqing.netty;

import com.yeqing.niodemo.Write;
import com.yeqing.util.ByteBufferUtil;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 使用NIO来理解阻塞模式-服务器
 */
public class Server {

    public static void blockTheServer() {
        // 1. 创建服务器
        try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
            ByteBuffer buffer = ByteBuffer.allocate(32);
            // 2. 绑定监听端口
            ssc.bind(new InetSocketAddress(7777));
            // 3. 存放建立连接的集合
            ArrayList<SocketChannel> channels = new ArrayList<>();
            while (true) {
                System.out.println("建立连接...");
                // 4. accept 建立客户端连接，用来和客户端之间通信
                final SocketChannel socketChannel = ssc.accept();
                System.out.println("建立连接成功...");
                channels.add(socketChannel);
                // 5. 接受客户端发送的数据
                for (SocketChannel channel : channels) {
                    System.out.println("正在读取数据...");
                    // 读取客户端的数据
                    channel.read(buffer);
                    // 重置到最开始
                    buffer.flip();
                    // 读出来
                    ByteBufferUtil.debugRead(buffer);
                    // 清空
                    buffer.clear();
                    System.out.println("数据读取完成...");
                }
            }
        } catch (IOException e) {
            System.out.println("出现异常...");
            e.printStackTrace();
        }
    }

    public static void nonBlockingServer() {
        try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
            final ByteBuffer buffer = ByteBuffer.allocate(32);
            // 设置端口
            ssc.bind(new InetSocketAddress(7777));
            // 设置非阻塞模式
            ssc.configureBlocking(false);

            // 存放建立连接的集合
            List<SocketChannel> channels = new ArrayList<>();
            while (true) {
//                System.out.println("建立连接...");
                // 建立客户端连接，用来和客户端之间通信
                final SocketChannel socketChannel = ssc.accept();
                // 设置非阻塞
                if (socketChannel == null) {
//                    System.out.println("没有客户端连接...");
                    continue;
                }
                socketChannel.configureBlocking(false);
                System.out.println("建立连接成功...");
                channels.add(socketChannel);
                for (SocketChannel channel : channels) {
                    System.out.println("正在读取数据...");
                    channel.read(buffer);
                    buffer.flip();
                    ByteBufferUtil.debugRead(buffer);
                    buffer.clear();
                    System.out.println("数据读取完成...");
                }
            }
        } catch (IOException e) {
            System.out.println("出现异常...");
            e.printStackTrace();
        }

    }

    /**
     * 使用Selector实现不阻塞服务端
     */
    public static void selectorServer() {
        try(
                ServerSocketChannel ssc = ServerSocketChannel.open();
                // 创建Selector管理多个Channel
                Selector selector = Selector.open()
        )
        {
            // 端口
            ssc.bind(new InetSocketAddress(7777));
            // 非阻塞
            ssc.configureBlocking(false);
            // 监听连接事件
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // 如果事件就绪，线程会被阻塞；反之不会被阻塞，从而避免了CPU空转
                // 返回值为就绪事件的个数
                int ready = selector.select();
                System.out.println("就绪事件个数：" + ready);

                // 获取所有事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    final SelectionKey key = iterator.next();
                    // 判断key的事件类型
                    if (key.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("获取客户端连接成功...");
                        // 设置为非阻塞模式，同时将连接的通道也注册到Selector中
                        socketChannel.configureBlocking(false);
                        ByteBuffer buffer = ByteBuffer.allocate(8);
                        // 每个客户端拥有自己的buffer，防止多个客户端共享同一个buffer导致数据错乱，设置附件buffer
                        socketChannel.register(selector, SelectionKey.OP_READ, buffer);
                    } else if (key.isReadable()) {
                        try {
                            // 读事件
                            SocketChannel channel = (SocketChannel) key.channel();
                            ByteBuffer clientBuffer = (ByteBuffer) key.attachment();

                            int read = channel.read(clientBuffer);
                            if (read == -1) {
                                System.out.println("客户端断开连接");
                                key.cancel();
                                channel.close();
                            } else {
                                // read == 0表示无新数据，无需处理
                                // 通过分隔符来分割buffer中的数据
                                NettyBase.split(clientBuffer);
                                // position表示缓冲区中可以读取的位置，limit表示缓冲区中可以写入的位置
                                if (clientBuffer.position() == clientBuffer.limit()) {
                                    // 如果缓冲区太小，进行翻倍
                                    ByteBuffer newBuffer = ByteBuffer.allocate(clientBuffer.capacity() * 2);
                                    // 把旧buffer的内容放到新buffer中
                                    clientBuffer.flip();
                                    newBuffer.put(clientBuffer);
                                    // 将新buffer放到key中作为附件
                                    key.attach(newBuffer);
                                    // 进行下一轮判断
                                }
                            }
                        } catch (Exception e) {
                            // 异常断开，取消时间
                            key.cancel();
                        }
                    }
                    // 处理完后移除
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 写事件
     */
    public static void writeSelectorServer() {
        try(
                ServerSocketChannel ssc = ServerSocketChannel.open();
                // 创建Selector管理多个Channel
                Selector selector = Selector.open()
        )
        {
            // 端口
            ssc.bind(new InetSocketAddress(7777));
            // 非阻塞
            ssc.configureBlocking(false);
            // 监听连接事件
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // 如果事件就绪，线程会被阻塞；反之不会被阻塞，从而避免了CPU空转
                // 返回值为就绪事件的个数
                int ready = selector.select();
                System.out.println("就绪事件个数：" + ready);

                // 获取所有事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    final SelectionKey key = iterator.next();
                    // 判断key的事件类型
                    if (key.isAcceptable()) {
                        final SocketChannel socketChannel = ssc.accept();
                        socketChannel.configureBlocking(false);

                        StringBuilder sb = new StringBuilder();
                        for (int i = 0; i < 3_000_000; i++) {
                            sb.append("a");
                        }
                        final ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());
                        // 注释掉，不在连接中写，写都放到selector中处理
//                        final int write = socketChannel.write(buffer);
//                        System.out.println("accept事件器写入..." + write);
                        // channel通道的容量小于buffer的容量，一次可能写不完，然后放到key的附件中
                        // 判断是否还有剩余内容
                        if (buffer.hasRemaining()) {
                            // 注册到selector，关注可写事件，将buffer设置到附件
                            socketChannel.register(selector, SelectionKey.OP_WRITE, buffer);
                        }
                    } else if (key.isWritable()) {
                        SocketChannel socket = (SocketChannel) key.channel();
                        // 获得事件
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        int write = socket.write(buffer);
                        // 写了之后不需要重新赋值，buffer写了会自动往后移动
                        System.out.println("write事件器写入..." + write);
                        // 如果已经完成了写操作，需要移除key中的附件，同时不再对写事件感兴趣
                        if (!buffer.hasRemaining()) {
                            key.attach(null);
                            key.interestOps(0);
                        }
                    }
                    // 处理完后移除
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void blockIOServer() {
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(7777));
            // 存放建立连接的集合
            List<SocketChannel> channels = new ArrayList<>();
            ByteBuffer buffer = ByteBuffer.allocate(16);
            serverSocketChannel.configureBlocking(false);
            while (true) {
                System.out.println("等待连接...");
                // 接受客户端连接
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel == null) {
                    continue;
                }
                socketChannel.configureBlocking(false);
                System.out.println(socketChannel);
                System.out.println("建立连接成功");
                channels.add(socketChannel);
                for (SocketChannel channel : channels) {
                    System.out.println("读取数据中...");
                    channel.read(buffer);
                    buffer.flip();
                    ByteBufferUtil.debugRead(buffer);
                    buffer.clear();
                    System.out.println("读取数据完成");
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void nonBlockingServer2() {
        try (
                ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
                Selector selector = Selector.open();
        ) {
            serverSocketChannel.bind(new InetSocketAddress(7777));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                System.out.println("等待连接...");
                int select = selector.select();
                System.out.println("就绪事件个数:" + select);

                // 所有事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();

                    if (key.isAcceptable()) {
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = channel.accept();
                        System.out.println("建立连接成功");
                        socketChannel.configureBlocking(false);
                        ByteBuffer buffer = ByteBuffer.allocate(2);
//                        socketChannel.register(selector, SelectionKey.OP_READ, buffer);
                    }
//                    else if (key.isReadable()) {
//                        try {
//                            SocketChannel channel = (SocketChannel) key.channel();
//                            ByteBuffer buffer = (ByteBuffer) key.attachment();
//                            int read = channel.read(buffer);
//                            if (read == -1) {
//                                System.out.println("数据已接收完毕，准备断开连接");
//                                key.cancel();
//                                channel.close();
//                            } else {
//                                System.out.println("处理数据中");
//                                // 处理粘包和半包
//                                NettyBase.split2(buffer);
//                                if (buffer.position() == buffer.limit()) {
//                                    ByteBuffer newBuffer = ByteBuffer.allocate(buffer.position() * 2);
//                                    buffer.flip();
//                                    newBuffer.put(buffer);
//                                    key.attach(newBuffer);
//                                }
//                            }
//                        } catch (IOException e) {
//                            key.cancel();
//                        }
//                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
//        selectorServer();
//        writeSelectorServer();
//        blockIOServer();
        nonBlockingServer2();
    }
}
